<?php

class File {
	const EditFileName = 'signwav-edit';

	private $_logger;
	private $_folder;
	private $_fileName;
	private $_md5hash = null;
	private $_md5AudioHash = null;
	private $_editing = false;
	private $_tags = null;
	private $_fileExtension = null;

	public function getFolder()
	{
		return $this->_folder;
	}

	public function getFileName()
	{
		return $this->_fileName;
	}

	public function getMd5Hash()
	{
		return $this->getOrComputeMd5Hash();
	}

	public function getMd5AudioOnlyHash()
	{
		return $this->getOrComputeAudioOnlyMd5Hash();
	}

	public function getFilePath()
	{
		return "{$this->_folder}/{$this->_fileName}";
	}

	public function getFileExtension()
	{
		if ($this->_fileExtension !== null) {
			return $this->_fileExtension;
		}

		$dot = $this->getLastIndexOf($this->getFileName(), '.');
		if ($dot > 0) {
			$this->_fileExtension = substr($this->getFileName(), $dot);
		} else {
			$this->_fileExtension = '';
		}

		return $this->_fileExtension;
	}
	
	public function __construct(Logger $logger, $folder, $fileName)
	{
		$this->_logger = $logger;
		$this->_folder = $folder;
		$this->_fileName = $fileName;

		$this->_logger->event(Logger::LevelDebug, 'File', "folder={$folder}|fileName={$fileName}");
	}

	/*******************************************************************************************************************

	 * STATIC METHODS
	 */

	public static function cleanUp()
	{
		self::removeTempFile();
	}

	public static function removeTempFile()
	{
		if (file_exists(self::EditFileName)) {
			unlink(self::EditFileName);
		}
	}
	public static function getTagValue($tags, $tag)
	{
		return (isset($tags['comments_html'][$tag][0]) ? $tags['comments_html'][$tag][0] : '');
	}


	/*******************************************************************************************************************

	 * PUBLIC METHODS
	 */

	public function beginEdit()
	{
		if ($this->_editing) {
			return true;
		}

		self::removeTempFile();

		if (!copy($this->getFilePath(), self::EditFileName)) {
			$this->_logger->event(Logger::LevelError, 'edit',
				'Unable to copy '
				. $this->getFilePath()
				. ' to '
				. self::EditFileName);
			return false;
		}

		$this->_editing = true;

		return true;
	}

	public function cancelEdit()
	{
		self::removeTempFile();
		$this->_editing = false;
	}

	public function removeTags($fileName)
	{
		// make sure we have them
		$this->getTags();

		$tagKiller = new getid3_writetags();
		$tagKiller->filename = $fileName;
		$tagsToDelete = array(
			'id3v1',
			'id3v2',
			'ape',
			'vorbiscomment',
			'metaflac',
			'lyrics3',
			'real'
		);

		$tagKiller->DeleteTags($tagsToDelete);
	}

	public function getTags()
	{
		if ($this->_tags !== null) {
			return $this->_tags;
		}

		$getID3 = new getID3;
		$this->_tags = $getID3->analyze($this->getFilePath());
		
		return $this->_tags;
	}

	public function saveTags($tags)
	{
		$this->_tags = $tags;

		$tagWriter = new getid3_writetags();
		$tagWriter->filename = $this->getFilePath();
		$tagWriter->tagformats = array(
			'id3v1');
//			'id3v2.2',
//			'id3v2.3',
//			'id3v2.4');
		$tagWriter->tag_encoding = 'UTF-8';

		getid3_lib::CopyTagsToComments($this->_tags);

		//@todo 'wash' the array.
		$tagWriter->tag_data = array(
			'title'        => array(self::getTagValue($this->_tags, 'title')),
			'artist'       => array(self::getTagValue($this->_tags, 'artist')),
			'album'        => array(self::getTagValue($this->_tags, 'album')),
			'publisher'    => self::getTagValue($this->_tags, 'publisher'),
			'track'        => array(self::getTagValue($this->_tags, 'track')),
			'genre'        => explode(self::getTagValue($this->_tags, 'genre'), ','), //array('peace', 'rock'),
		);
		
		$tagWriter->remove_other_tags = false;

		$result = $tagWriter->WriteTags();
		return $result;
	}

	/**
	 * @param $folder the destination folder
	 * @param $fileName the destination file name
	 * @param $ownCopy if true, this instance will be the copy, else the original file path
	 * @return bool true if successfull
	 */
	public function copyFile($folder, $fileName, $ownCopy = true)
	{
		$source = $this->_editing ? self::EditFileName : $this->getFilePath();
		$destination = "{$folder}/{$fileName}";

		if (is_file($destination)) {
			$this->_logger->event(Logger::LevelError, 'copyFile', "unable to copy {$source} to {$destination}. the destination already exists.");
			return false;
		}

		copy($source, $destination);

		if ($ownCopy) {
			$this->_folder = $folder;
			$this->_fileName = $fileName;
		}

		return true;
	}
	/*******************************************************************************************************************

	 * PRIVATE METHODS
	 */

	/**
	 * lazy loads the computation of md5 hash computation
	 * @return null
	 */
	private function getOrComputeMd5Hash()
	{
		if ($this->_md5hash !== null) {
			return $this->_md5hash;
		}

		// this hashes the meta data as well
		// we need to extract just the data and hash that
		// as the idea is to find duplicate "audio"
		$this->_md5hash = md5_file($this->getFilePath());

		return $this->_md5hash;
	}

	private function getOrComputeAudioOnlyMd5Hash()
	{
		if ($this->_md5AudioHash !== null) {
			return $this->_md5AudioHash;
		}

		$this->_logger->display('.', false);

		if (!$this->beginEdit()) {
			$this->_md5AudioHash = $this->getMd5Hash();
			return $this->_md5AudioHash;
		}

		$this->removeTags(self::EditFileName);

		// now get md5 hash of audio only data
		$this->_md5AudioHash = md5_file(self::EditFileName);

		return $this->_md5AudioHash;
	}

	private function getLastIndexOf($haystack, $needle, $offset = null)
	{
		if (strpos($haystack,$needle,$offset) === false) {
            return false;
		}

		return strlen($haystack)
           - strpos( strrev($haystack) , strrev($needle) , $offset)
           - strlen($needle);
	}
}
